#!/usr/bin/env bash
# Summary: Rehash sbtenv shims (run this after installing executables)

set -e
test -n "${SBTENV_DEBUG}" && set -x

SHIM_PATH="${SBTENV_ROOT}/shims"
PROTOTYPE_SHIM_PATH="${SHIM_PATH}/.sbtenv-shim"

# Create the shims directory if it doesn't already exist.
mkdir -p "${SHIM_PATH}"

# Ensure only one instance of sbtenv-rehash is running at a time by
# setting the shell's `noclobber` option and attempting to write to
# the prototype shim file. If the file already exists, print a warning
# to stderr and exit with a non-zero status.
set -o noclobber
{ echo > "${PROTOTYPE_SHIM_PATH}"
} 2> /dev/null ||
  { if [ -w "${SHIM_PATH}" ]; then
    echo "sbtenv: cannnot rehash: ${PROTOTYPE_SHIM_PATH} exists"
  else
    echo "sbtenv: cannnot rehash: ${SHIM_PATH} isn't writable"
  fi
  exit 1
} >& 2
set +o noclobber

# If we were able to obtain a lock, register a trap to clean up the
# prototype shim when the process exits.
trap remove_prototype_shim EXIT

remove_prototype_shim() {
  rm -f "${PROTOTYPE_SHIM_PATH}"
}

# The prototype shim file is a script that re-execs itself, passing
# its filename and any arguments to `sbtenv exec`. This file is
# hard-linked for every executable and then removed. The linking
# technique is fast, uses less disk space than unique files, and also
# serves as a locking mechanism.
create_prototype_shim() {
  cat > "${PROTOTYPE_SHIM_PATH}" <<SH
#!/usr/bin/env bash
set -e
test -n "\${SBTENV_DEBUG}" && set -x

program="\${0##*/}"
if [ "\${program}" = "sbt" ]; then
  for arg; do
    case "\${arg}" in
      -- ) break ;;
    */* )
      if [ -f "\${arg}" ]; then
        export SBTENV_DIR="\${arg%/*}"
        break
      fi
      ;;
  esac
done
fi

export SBTENV_ROOT="${SBTENV_ROOT}"
exec "$(command -v sbtenv)" exec "\${program}" "\${@}"
SH
  chmod +x "${PROTOTYPE_SHIM_PATH}"
}

# If the contents of the prototype shim file differ from the contents
# of the first shim in the shims directory, assume sbtenv has been
# upgraded and the existing shims need to be removed.
remove_outdated_shims() {
  for shim in *; do
    if ! diff "${PROTOTYPE_SHIM_PATH}" "${shim}" > /dev/null 2>& 1; then
      for shim in *; do rm -f "${shim}"; done
    fi
    break
  done
}

# The basename of each argument passed to `make_shims` will be
# registered for installation as a shim. In this way, plugins may call
# `make_shims` with a glob to register many shims at once.
make_shims() {
  local shims=("${@}")

  for file in "${shims[@]}"; do
    local shim="${file##*/}"
    register_shim "${shim}"
  done
}

# Create an empty array for the list of registered shims and an empty
# string to use as a search index.
registered_shims=()
registered_shims_index=""

# We will keep track of shims registered for installation with the
# global `registered_shims` array and with a global search index
# string. The array will let us iterate over all registered shims. The
# index string will let us quickly check whether a shim with the given
# name has been registered or not.
register_shim() {
  local shim="${@}"
  registered_shims["${#registered_shims[@]}"]="${shim}"
  registered_shims_index="${registered_shims_index}/${shim}/"
}

# To install all the registered shims, we iterate over the
# `registered_shims` array and create a link if one does not already
# exist.
install_registered_shims() {
  local shim
  for shim in "${registered_shims[@]}"; do
    [ -e "${shim}" ] || ln -f "${PROTOTYPE_SHIM_PATH}" "${shim}"
  done
}

# Once the registered shims hava been installed, we make a second pass
# over the contents of the shims directory. Any file that is present
# in the directory but has not been registered as a shim should be
# removed.
remove_stale_shims() {
  local shim
  for shim in *; do
    if [[ "${registered_shims_index}" != *"/${shim}/"* ]]; then
      rm -f "${shim}"
    fi
  done
}

# Change to the shims directory.
cd "${SHIM_PATH}"
shopt -s nullglob

# Create the prototype shim, then register shims for all known
# executables.
create_prototype_shim
remove_outdated_shims
make_shims ../versions/*/sbt/bin/sbt

# Restore the previous working directory.
cd "${OLDPWD}"

# Allow plugins to register shims.
OLDIFS="${IFS}"
IFS=$'\n' scripts=(`sbtenv-hooks rehash`)
IFS="${OLDIFS}"

for script in "${scripts[@]}"; do
  source "${script}"
done

# Change back to the shims directory to install the registered shims
# and remove stale shims.
cd "${SHIM_PATH}"
install_registered_shims
remove_stale_shims

